﻿// -----------------------------------------------------------------------
// <copyright file="Response.cs" company="Softserve">
// Response
// Made by VK
// </copyright>
// -----------------------------------------------------------------------
namespace WebServer.BusinessLogic
{
    using System;
    using System.Globalization;
    using System.Text;
    using WebServer.BusinessLogic.Web;

    /// <summary>
    /// Response is responsible to serve data needed for RequestBuilder and Communicator to create a reply of our webserver for internet browser
    /// </summary>
    public class Response
    {
        /// <summary>
        ///  Initializes a new instance of the <see cref="Response" /> class.
        /// </summary>
        public Response()
        {
            this.Data = null;
            this.HttpVersion = Helpers.WebserverConstants.HttpVersion10;
            this.HttpResponseCode = 200;
            this.HttpResponseCodeDescription = "OK";
            this.Cookies = new WebsiteCookies();
            
            this._configurator = Configurator.Instance;
            this._sessionManager = SessionManager.Instance;
        }

        private Configurator _configurator;
        private SessionManager _sessionManager;


        /// <summary>
        /// Initializes a new instance of the <see cref="Response" /> class.
        /// Constructor that should initialiaze all values that can have a setter
        /// </summary>
        /// <param name="mimeType">MimeType of the data</param>
        /// <param name="httpVersion">Version, HTTP/1.0 or HTTP/1.1</param>
        /// <param name="data">Byte array of data to respond with in body</param>
        public Response(string mimeType, string httpVersion, byte[] data) : this()
        {
            this.MimeType = mimeType;
            this.HttpVersion = httpVersion;
            this.Data = data;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Response" /> class.
        /// </summary>
        /// <param name="request">http request</param>
        public Response(Request request) : this()
        {
            if (request != null)
            {
                this.MimeType = "text/html";
                this.HttpVersion = request.HttpVersion;
                this.KeepAlive = request.KeepAlive;
            }
        }

        /// <summary>
        /// Constructor for Configurator and SessionManager injection
        /// </summary>
        /// <param name="configurator">Configurator to inject</param>
        /// <param name="sessionManager">SessionManager to inject</param>
        public Response(Configurator configurator, SessionManager sessionManager) : this()
        {
            _configurator = configurator;
            _sessionManager = sessionManager;
        }

        /// <summary>
        /// Enum for redirect
        /// </summary>
        public enum RedirectMethodEnum
        {
            /// <summary>
            /// Temporary redirect
            /// </summary>
            Temporary
        }

        /// <summary>
        /// Gets or sets bytes of the Data that are needed to be responded with
        /// </summary>
        public byte[] Data { get; /*private*/ set; }

        /// <summary>
        /// Gets or sets mime type of file, for example: "text/html" or "image/gif"
        /// </summary>
        public string MimeType { get; /*private*/ set; }

        /// <summary>
        /// Gets or sets version of the HTTP protocol. Either HTTP/1.0 OR HTTP/1.1
        /// </summary>
        public string HttpVersion { get; /*private*/ set; }

        /// <summary>
        /// Gets or sets Hostname, for example: www.google.com
        /// </summary>
        public string Host { get; set; }

        /// <summary>
        /// Gets or sets bytes of the data webserver is returning to the client, start position
        /// </summary>
        public long RangeStart { get; set; }

        /// <summary>
        /// Gets or sets HTTP Response code. Default "200"
        /// </summary>
        public int HttpResponseCode { get; set; }

        /// <summary>
        /// Gets or sets HTTP Response description for the code. Default "OK"
        /// </summary>
        public string HttpResponseCodeDescription { get; set; }

        /// <summary>
        /// Gets or sets Connection status. Can be "close" or empty
        /// </summary>
        public string ConnectionStatus { get; set; }

        /// <summary>
        /// Gets or sets Last modified date of file. Handler should set this value
        /// </summary>
        public DateTime LastModified { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether if Response Accepts ranges. true will set Accept-Range header to "bytes", false to "none"
        /// </summary>
        public bool AcceptsRanges { get; set; }

        /// <summary>
        /// Gets or sets Encoding of responce content
        /// </summary>
        public string ContentEncoding { get; set; }

        /// <summary>
        /// Gets or sets Set-Cookie cookies
        /// </summary>
        public WebsiteCookies Cookies { get; set; }

        /// <summary>
        /// Gets or sets the Location path
        /// If set, initiates redirect to the Location path
        /// </summary>
        public string Location { get; set; }

        /// <summary>
        /// Indicates that connection should not be dropped by server
        /// </summary>
        public bool KeepAlive { get; set; }

        /// <summary>
        /// Set redirect to response
        /// </summary>
        /// <param name="location">path location</param>
        /// <param name="redirectMethod">redirect Method</param>
        /// <param name="request">Request object needed to create session value for redirect</param>
        public void SetRedirect(string location, RedirectMethodEnum redirectMethod, Request request)
        {
            if (_configurator.IsCookielessSession && !location.StartsWith("http", StringComparison.OrdinalIgnoreCase))
            {
                var session = _sessionManager.GetCurrentSession(request);
                if (session != null)
                {
                    // got the session
                    location = string.Format(CultureInfo.CurrentCulture, "/[{0}]/{1}", session.SessionKey, location.TrimStart(new[] { '/' }));
                }
            }

            this.Location = location;
            switch (redirectMethod)
            {
                case RedirectMethodEnum.Temporary:
                    this.HttpResponseCode = 302;
                    this.HttpResponseCodeDescription = "Found";
                    this.Data = this.GetHtmlBytesForLocationHeader();
                    break;
            }
        }

        /// <summary>
        /// Creates 302 Found temporary redirect
        /// </summary>
        /// <param name="location">redirect location</param>
        /// <param name="request">Request object needed to create session value for redirect</param>
        public void SetRedirect(string location, Request request)
        {
            this.SetRedirect(location, RedirectMethodEnum.Temporary, request);
        }

        /// <summary>
        /// Settings httpResponse = 500 when server errors happen
        /// </summary>
        public void SetServerError()
        {
            this.HttpResponseCode = 500;
            this.HttpResponseCodeDescription = "Internal Server Error";
        }

        /// <summary>
        /// Setting acess denied
        /// </summary>
        public void SetAccessDenied()
        {
            this.HttpResponseCode = 401;
            this.HttpResponseCodeDescription = "Access Denied";
            this.Data = Encoding.UTF8.GetBytes("Access Denied");
        }

        /// <summary>
        /// Gets HTML page with encoded bytes for specific text for old browsers that doesn't support some functionality, like "Location" header
        /// </summary>
        /// <returns>Encoded bytes</returns>
        private byte[] GetHtmlBytesForLocationHeader()
        {
            string text = string.Format("{0} {1}", this.HttpResponseCode, this.HttpResponseCodeDescription);
            string location = this.Location;

            string htmlMessage = string.Format(
                @"<HTML><HEAD><meta http-equiv=""content-type"" content=""text/html;charset=utf-8"">
<TITLE>{0}</TITLE></HEAD><BODY>
<H1>{0}</H1>
The document has moved
<A HREF=""{1}"">here</A>.
</BODY></HTML>",
                text,
                location);

            return Encoding.UTF8.GetBytes(htmlMessage);
        }
    }
}
